﻿using System;
using System.Drawing;
using System.Linq;
using GraphDrawing.Graph.Model;
using GraphDrawing.Graph.View;

namespace GraphDrawing.Concrete
{
    public class LogicalOrBlockFactory : IBlockFactory
    {
        static readonly RectangleShape BLOCK_SHAPE = new RectangleShape(70, 100);
        static readonly CircleShape INPUT_SLOT_SHAPE = new CircleShape(8);
        static readonly CircleShape OUTPUT_SLOT_SHAPE = new CircleShape(10);

        readonly ManualLayoutManager slotLayout;
        readonly LogicalOrBlockView view;

        public LogicalOrBlockFactory()
        {
            slotLayout = new ManualLayoutManager();
            slotLayout.SetPosition(0, new Point(-10, 23));
            slotLayout.SetPosition(1, new Point(-10, 56));
            slotLayout.SetPosition(2, new Point(BLOCK_SHAPE.width - OUTPUT_SLOT_SHAPE.radius, 50 - OUTPUT_SLOT_SHAPE.radius));
            view = new LogicalOrBlockView();
        }

        BlockViewModel IBlockFactory.Create()
        {
            var logic = new LogicalOrModel();
            var block = new BlockModel(BLOCK_SHAPE);
            block.SetSlots(slotLayout, new SlotModel(new InputSlotStrategy(0, logic), INPUT_SLOT_SHAPE), new SlotModel(new InputSlotStrategy(1, logic), INPUT_SLOT_SHAPE),
                           new SlotModel(new OutputSlotStrategy(0, logic), OUTPUT_SLOT_SHAPE));
            return new BlockViewModel(block, view);
        }
    }

    public class LogicalOrModel : IBlockLogic
    {
        readonly Func<bool>[] inputs = new Func<bool>[2];

        public Type InputType
        {
            get { return typeof (Func<bool>); }
        }

        public bool CanAccept(int inputIndex, Type contentType)
        {
            return inputIndex >= 0 && inputIndex < 2 && contentType == InputType;
        }

        public bool Requires(int inputIndex)
        {
            return true;
        }

        public void Connect(int inputIndex, object value)
        {
            if (!CanAccept(inputIndex, value.GetType()))
                throw new InvalidOperationException();
            inputs[inputIndex] = (Func<bool>) (value);
        }

        public void Disconnect(int inputIndex)
        {
            if (inputIndex < 0 || inputIndex > 2)
                return;
            inputs[inputIndex] = null;
        }

        public object Provide(int outputIndex)
        {
            if (outputIndex != 0)
                throw new InvalidOperationException("invalid slot id");
            return (Func<bool>) GetValue;
        }

        public bool Validate()
        {
            return inputs.All(action => action != null);
        }

        /// <summary>
        /// Возвращает значение слота.
        /// </summary>
        bool GetValue()
        {
            return inputs.Aggregate(false, (value, func) => value | func());
        }
    }

    public class LogicalOrBlockView : RectangleBlockView
    {
        public LogicalOrBlockView()
        {
            Default.Color = Color.DodgerBlue;
            Selected.Color = Color.FromArgb(64, Color.DodgerBlue);
            Selected.Border.Width = 1.2f;

            var slotView = new CircleSlotView
                               {
                                   Default = {Color = Color.WhiteSmoke},
                                   Pressed = {Color = Color.DarkGray},
                                   Acceptable = {Color = Color.DarkSeaGreen},
                                   Unacceptable = {Color = Color.DarkRed}
                               };
            BindSlotView(0, slotView);
            BindSlotView(1, slotView);
            BindSlotView(2, slotView);
        }
    }
}